﻿using System;
using System.Collections.Generic;
using System.Timers;
using System.Linq;

#if !ANB_STANDALONE
using UnityEngine;
#endif


namespace AnB.Common
{
    public class Timeout
    {
        private SortedDictionary<long, Action> _Timeouts = new SortedDictionary<long, Action>();
        private Timer _Timer;
        private DateTime _StartTime;
        public Timeout()
        {
        }

#if ANB_STANDALONE
        public Timeout(long interval)
        {
            _StartTime = DateTime.Now;
            _Timer = new Timer(interval);
            _Timer.Elapsed += (source, e) =>
                {
                    Check();
                };
            _Timer.Enabled = true;
        }
        public static Timeout Instance = new Timeout(100);
#else
        public static Timeout Instance = new Timeout();
#endif

        /// <summary>
        /// 
        /// </summary>
        /// <param name="timeout">ms</param>
        /// <param name="action"></param>
        /// <returns></returns>
        public long Add(long timeout, Action action)
        {
            long expire = Ticks / 10 + timeout * 1000; // us
            lock (this)
            {
                return _Add(expire, action);
            }
        }

        public bool Remove(long expire)
        {
            lock (this)
            {
                return _Timeouts.Remove(expire);
            }
        }

        protected long Ticks
        {
            get
            {
                return (DateTime.Now - _StartTime).Ticks;
            }
        }

        public void Check()
        {
            long now = Ticks / 10; // us
            var actions = new List<KeyValuePair<long, Action>>();
            lock (this)
            {
                while (_Timeouts.Any())
                {
                    var kv = _Timeouts.ElementAt(0);
                    if (kv.Key > now)
                    {
                        break;
                    }

                    _Timeouts.Remove(kv.Key);
                    actions.Add(kv);
                }
            }
            foreach (var kv in actions)
            {
                try
                {
                    kv.Value();
                }
                catch (Exception e)
                {
                    Log.Get().ErrorFormat("timeout callback exception {0}\n{1}", e.Message, e.StackTrace);
                }
            }
        }

        protected long _Add(long expire, Action action)
        {
            try
            {
                _Timeouts.Add(expire, action);
                return expire;
            }
            catch (ArgumentException)
            {
                return _Add(expire + 1, action);
            }
        }
    }
}
